﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ZenInterface
{

    enum serialState
    {
        NotConnected = 0,
        Ready = 1,
        Error = -1,
        CantFindDevice = -2,
        Scanning = 2,
        Connected = 3,
        Connecting = 4,
        Closing = 5,
        Timing = 6

    };

    public partial class serialPortControllerClass : Form
    {

        #region members
        int state;
        Properties.Settings settings;
        String inputBuffer;
        String extraString;
        #endregion

        public serialPortControllerClass(Properties.Settings inSettings)
        {
            InitializeComponent();
            state = (int)serialState.NotConnected;
            if (serialPort.IsOpen)
            {
                state = (int)serialState.Ready;
            }
            settings = inSettings;
            settings.userSerialConnected = serialPort.IsOpen;
            extraString = String.Empty;
            inputBuffer = String.Empty;
        }
        
        #region functions
        public int update()
        {
            radioButtonConnect.Checked = serialPort.IsOpen;
            settings.userSerialConnected = serialPort.IsOpen;
            labelUpdateRate.Text = settings.userSerialUpdateRate.ToString();
            labelUpdateFrequency.Text = settings.userSerialUpdateFrequency.ToString();
            return state;
        }
        public String getInputData()
        {
            String tempInputBuffer = inputBuffer;
            //clear out the data for the next time
            inputBuffer = String.Empty;
            return tempInputBuffer;
        }
        public String stateTranslate(int inState)
        {
            String outputBuffer = String.Empty;

            switch (inState)
            {
                case ((int)serialState.NotConnected):
                    outputBuffer = Properties.Resources.stringNotConnected;// "Not Connected";
                    break;

                case ((int)serialState.Connected):
                    outputBuffer = Properties.Resources.stringConnected;// "Connected";
                    break;

                case ((int)serialState.Error):
                    outputBuffer = Properties.Resources.stringError + extraString;//"Error";
                    break;

                case ((int)serialState.CantFindDevice):
                    outputBuffer = Properties.Resources.stringCantFindDevice;// "Can't find device";
                    break;

                case ((int)serialState.Scanning):
                    outputBuffer = Properties.Resources.stringScanning + extraString;// "Scanning";
                    break;

                case ((int)serialState.Connecting):
                    outputBuffer = Properties.Resources.stringScanning + extraString;//"Connecting";
                    break;

                case ((int)serialState.Closing):
                    outputBuffer = Properties.Resources.stringClosing;// "Closing";
                    break;

                case ((int)serialState.Timing):
                    outputBuffer = Properties.Resources.stringTiming + extraString;// Analyzing Timing
                    break;

                case ((int)serialState.Ready):
                    outputBuffer = Properties.Resources.stringReady;
                    break;

                default:
                    outputBuffer = Properties.Resources.stringUnknown;// "???";
                    break;
            }

            toolStripStatusLabel.Text = outputBuffer;

            return outputBuffer;
        }
        public bool closeSerialPort()
        {
            serialPort.Close();
            return true;
        }
        #region connection background worker
        private bool autoConnect(BackgroundWorker worker, DoWorkEventArgs e)
        {
            //autoconnect
            String buffer;
            String temp;
            DateTime wait;
            DateTime delay;
            int numberOfPorts = System.IO.Ports.SerialPort.GetPortNames().Length;

            //get the names of all the serial ports
            for (int i = 0; i < numberOfPorts; i++)
            {
                //get the current string name
                String s = System.IO.Ports.SerialPort.GetPortNames()[i];

                buffer = s;

                worker.ReportProgress(((i * 100) / numberOfPorts), buffer);
                buffer = String.Empty;

                if (serialPort.IsOpen)
                {
                    serialPort.Close();
                }

                serialPort.PortName = s;

                try
                {
                    serialPort.Open();
                }
                catch
                {
                    serialPort.Close();
                }

                delay = DateTime.Now.Add(TimeSpan.FromMilliseconds(settings.userSerialMillisecondDelay));
                while (DateTime.Now < delay)
                {
                    //check for string name
                    try
                    {
                        temp = serialPort.ReadExisting();
                    }
                    catch
                    {
                        //nothing
                        temp = String.Empty;
                    }
                    //if the zen keyword is found we are on the correct port, and we are done
                    if (temp.Contains("ZEN"))
                    {
                        serialPort.DiscardInBuffer();
                        
                        return true;
                    }
                    wait = DateTime.Now.Add(TimeSpan.FromMilliseconds(100));
                    while (DateTime.Now < wait) ;
                    //give the buffer time to fill up
                }
            }

            //zen keyword is not found
            if (serialPort.IsOpen)
            {
                serialPort.Close();
            }
            return false;
        }
        private void connectBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            //set up background worker here
            BackgroundWorker worker = sender as BackgroundWorker;
            e.Result = autoConnect(worker, e);
        }
        private void connectBackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            //toolStripStatusLabel.Text = ((String)e.UserState);
            state = (int)serialState.Connecting;
            extraString = ((String)e.UserState);
        }
        private void connectBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            extraString = String.Empty;
            inputBuffer = String.Empty;
            if (((bool)e.Result) == true)
            {
                //device is connected
                state = (int)serialState.Ready;
                settings.userSerialUpdateRate = synchronousTimingAnalysis();
                buttonAnalyzeTiming.Enabled = true;
                this.serialPort.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(this.serialPort_DataReceived);
            }
            else
            {
                state = (int)serialState.CantFindDevice;
            }
        }
        private void invoke(bool p)
        {
            //connect up to main thread
        }
        private int synchronousTimingAnalysis()
        {
            //worker.ReportProgress(((i * 100) / numberOfPorts), buffer);
            DateTime delay;
            String temp;
            //char[] stringTokens = { '*' };
            //String[] tempStrings;
            DateTime start = DateTime.Now;
            DateTime end = DateTime.Now;

            state = (int)serialState.Timing;

            int timeDelta = 0;

            //bypass the serial interrupt for a while
            //serialPort.ReadBufferSize = 4096;
            try
            {

                serialPort.DiscardInBuffer();
                temp = serialPort.ReadExisting();
                delay = DateTime.Now.Add(TimeSpan.FromMilliseconds(1000));
                while (DateTime.Now < delay)
                {
                    //check for string name
                    if (serialPort.BytesToRead > 0)
                    {
                        //append the new data
                        temp += serialPort.ReadExisting();
                        //nom away the *'s !!! NOM NOM NOM
                        while (temp.Contains("*"))
                        {
                            timeDelta++;
                            if (temp.Length > (temp.IndexOf("*")))
                            {
                                //if there is a next location go to it
                                temp = temp.Substring(temp.IndexOf("*") + 1);
                            }

                            else
                            {
                                temp = String.Empty;
                            }
                            
                        }
                    }

                    //DateTime wait = DateTime.Now.Add(TimeSpan.FromMilliseconds(1));
                    //while (DateTime.Now < wait) ;
                    //give the buffer time to fill up

                }
                //serialPort.ReadBufferSize = settings.serialInputBufferThreshold;
                state = (int)serialState.Ready;
                return timeDelta;
            }
            catch
            {
                //serialPort.ReadBufferSize = settings.serialInputBufferThreshold;
                state = (int)serialState.Ready;
                return -1;
            }
        }
        #endregion
        #region cross threading intersection
        #endregion
        #endregion
        #region events
        private void buttonConnect_Click(object sender, EventArgs e)
        {
            //attempt connect with the specified serial port
            if (serialPort.IsOpen)
            {
                try
                {
                    serialPort.Close();
                }
                catch
                {
                    extraString = "Can't close port";
                    state = (int)serialState.Error;
                }
            }

            try
            {
                serialPort.PortName = comboBoxComPorts.Items[comboBoxComPorts.SelectedIndex].ToString();
                serialPort.Open();
            }
            catch
            {
                extraString = "Can't open port";
                state = (int)serialState.Error;
            }
            
        }
        private void buttonAnalyzeTiming_Click(object sender, EventArgs e)
        {
            //analyze the timing of the input signal to determine period, and frequency
            settings.userSerialUpdateRate = synchronousTimingAnalysis();

        }
        private void buttonAutoConfig_Click(object sender, EventArgs e)
        {
            //attemp to automatically configure serial port, device must be attached
            //if we are analyzign timing, let if finish first
            if (state != (int)serialState.Timing)
            {
                connectBackgroundWorker.RunWorkerAsync();
            }
        }
        private void buttonSaveConfiguration_Click(object sender, EventArgs e)
        {
            settings.userSerialComPort = serialPort.PortName;
            settings.userSerialEnableAutoConnect = checkBoxEnableAutoConnect.Checked;
        }
        private void buttonScanComPorts_Click(object sender, EventArgs e)
        {
            foreach (string s in System.IO.Ports.SerialPort.GetPortNames())
            {
                //
                comboBoxComPorts.Items.Add(s);

            }
            comboBoxComPorts.SelectedIndex = 0;
        }
        #endregion
        #region interrupt
        private void serialPort_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            if (state != (int)serialState.Timing)
            {
                inputBuffer += serialPort.ReadExisting();
            }
        }
        #endregion

        private void serialPortControllerClass_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (e.CloseReason == CloseReason.UserClosing)
            {
                e.Cancel = true;
                this.Hide();
            }
        }




    }
}
